package com.qgy.springsecurity.utils;

import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;

/**
 * JWT 工具类，用于生成、解析和验证 JWT（JSON Web Token）
 */
public class JwtUtil {

    /**
     * JWT 默认过期时间：60 分钟（单位毫秒）
     */
    public static final Long JWT_TTL = 60 * 60 * 1000L;

    /**
     * JWT 签名密钥（Base64 编码前的原始字符串）
     * 每次重启应用都会变化，实际生产环境应使用固定密钥或从配置文件中读取
     */
    public static final String JWT_KEY = Base64.getEncoder().encodeToString(Keys.secretKeyFor(SignatureAlgorithm.HS256).getEncoded());

    /**
     * 生成一个不带连字符的 UUID 字符串，通常用作 JWT 的唯一标识 ID（jti）
     *
     * @return 去除 "-" 后的 UUID 字符串
     */
    public static String getUUID() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 创建 JWT 令牌（默认使用随机生成的 ID）
     *
     * @param subject   主题（通常是用户信息，如用户名或用户ID）
     * @param ttlMillis 过期时间（单位：毫秒），若为 null 则使用默认值 JWT_TTL
     * @return JWT 令牌字符串
     */
    public static String createJWT(String subject, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());
        return builder.compact(); // 构建并返回紧凑格式的 JWT 字符串
    }

    /**
     * 构建 JWT 对象的基础方法
     *
     * @param subject 主题信息
     * @param ttlMillis 过期时间（毫秒）
     * @param uuid      JWT 的唯一标识（jti）
     * @return 返回构建好的 JwtBuilder 对象
     */
    private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // 使用 HS256 算法签名
        SecretKey secretKey = generalKey(); // 获取签名密钥

        long nowMillis = System.currentTimeMillis(); // 当前时间戳
        Date now = new Date(nowMillis); // 当前时间对象

        if (ttlMillis == null) {
            ttlMillis = JWT_TTL; // 若未指定过期时间，则使用默认值
        }

        long expMillis = nowMillis + ttlMillis; // 计算过期时间戳
        Date expDate = new Date(expMillis); // 设置过期时间

        // 构建 JWT
        return Jwts.builder()
                .setId(uuid)               // 设置 JWT ID（唯一标识）
                .setSubject(subject)       // 设置主题（如用户ID）
                .setIssuer("服务器")       // 设置签发者
                .setIssuedAt(now)          // 设置签发时间
                .signWith(signatureAlgorithm, secretKey) // 设置签名算法和密钥
                .setExpiration(expDate);   // 设置过期时间
    }

    /**
     * 创建 JWT 令牌（使用自定义 ID）
     *
     * @param id        自定义的 JWT ID（jti）
     * @param subject   主题信息（如用户名）
     * @param ttlMillis 过期时间（毫秒）
     * @return JWT 令牌字符串
     */
    public static String createJWT(String id, String subject, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);
        return builder.compact();
    }

    /**
     * 根据 JWT_KEY 生成 Base64 解码后的 SecretKey（用于签名和验证）
     *
     * @return SecretKey 密钥对象
     */
    public static SecretKey generalKey() {
        byte[] encodeKey = Base64.getDecoder().decode(JWT_KEY); // 将密钥解码为字节数组
        // 使用 AES 算法构造 SecretKey
        return new SecretKeySpec(encodeKey, 0, encodeKey.length, "HmacSHA256");
    }

    /**
     * 解析并验证 JWT 字符串，返回其包含的 Claims（负载内容）
     *
     * @param jwt 待解析的 JWT 字符串
     * @return 包含 JWT 负载信息的 Claims 对象
     * @throws Exception 如果 JWT 无效或签名错误会抛出异常
     */
    public static Claims parseJWT(String jwt) {
        SecretKey secretKey = generalKey(); // 获取签名密钥
        return Jwts.parser() // 创建 JWT 解析器
                .setSigningKey(secretKey) // 设置签名密钥
                .parseClaimsJws(jwt) // 解析并验证 JWT
                .getBody(); // 获取 JWT 中的 Claims（即 payload）
    }

    public static <T> T getSubject(String jwt, Class<T> clazz) {
        try {
            // 构建解析器
            JwtParser jwtParser = Jwts.parserBuilder()
                    .setSigningKey(generalKey())
                    .build();

            // 解析 JWT
            Jws<Claims> jws = jwtParser.parseClaimsJws(jwt);
            Claims claims = jws.getBody();

            // 获取 subject 字段内容（通常是 JSON 字符串）
            String subjectJson = claims.getSubject();

            if (subjectJson == null || subjectJson.isEmpty()) {
                throw new IllegalArgumentException("Subject is empty in the JWT token.");
            }

            // 使用 Jackson 反序列化成目标类型
            return JSON.parseObject(subjectJson,clazz);

        } catch (Exception e) {
            throw new RuntimeException("token解析错误", e);
        }
    }
}
